Modifying original code

Original file: Fig3_4_6_ExtFig5-6-10_RPM_RPMA_Allos_CellTag.R

Only CellTag analysis included in this notebook.

Visualizing Celltag/clone data

From table above, added whether clones were robust or not
Robust defined as >5 cells per clone post-QC.
CellTag metadata: clone information can also be found in Supplementary Table 4 of Ireland et al, 2025

Idents(TBO_seurat) <- 'Robust'
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$UnID)
        
         RPMA_Allo RPM_Allo3 RPM_Allo4 RPM_Allo_New
  No          6659      5244      6523         2227
  Robust      3501      1474       337          653
table(TBO_seurat@meta.data$Robust, TBO_seurat@meta.data$GenoCT)
        
         RPMA_CTpreCre RPM_CTpostCre RPM_CTpreCre
  No              6659         11767         2227
  Robust          3501          1811          653

Identify robust clones

table(clones@meta.data$Robust, clones@meta.data$Genotype)
        
          RPM RPMA
  Robust 2464 3501

Assess clones by leiden cluster

Fig. 3d

First, just look at bar graph, no particular order #

Idents(clones) <- 'leiden_scVI_1.2'

x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))

# proportions$Cluster
colnames(proportions)<-c("Cluster", "Sample", "Frequency")

# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) + 
  geom_bar(position="stack", stat="identity")

p + scale_fill_manual(values=my_colors) + 
  theme_bw()+ theme(axis.text.y = element_text(size=20), 
                    axis.text.x=element_text(size=14), axis.title.x =element_text(size=14), 
                    axis.title.y = element_text(size=18), legend.text = element_text(size=12), 
                    legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90)


dat <- p$data

Transform data to perform hierarchical (agglomerative) clustering

# Pivot to wide format: Cluster × Sample
mat <- dat %>%
  pivot_wider(names_from = Sample, values_from = Frequency, values_fill = 0) %>%
  tibble::column_to_rownames("Cluster") %>%
  as.matrix()

mat_norm <- prop.table(mat, margin = 1)  # normalize each row to sum to 1

Hierarchical (agglomerative) clustering of clones with default parameters in pheatmap()

hm <- pheatmap::pheatmap(t(mat), cutree_rows = 1, cutree_cols = 8, cellwidth = 5, 
                         cellheight = 5, fontsize = 8,
                         cluster_rows=FALSE, border_color=NA, 
                         color = colorRampPalette(c("darkturquoise","black","red2"))(30))


clone_order <- colnames(t(mat))[hm$tree_col$order]

Fig 3d

Clustered clones by proportions of cells in Leiden clusters

# Now, plot clones by Leiden, ordered, final # (Fig. 3d)
Idents(clones)<-'leiden_scVI_1.2'

x <- table(clones@meta.data$CellTag_Clone,Idents(clones))
proportions <- as.data.frame(100*prop.table(x, margin = 1))

colnames(proportions) <- c("Cluster", "Sample", "Frequency")
proportions$Cluster <- factor(proportions$Cluster, clone_order)

# Stacked
p <- ggplot(proportions, aes(fill=Sample, y=Frequency, x=Cluster)) + 
    geom_bar(position="stack", stat="identity")

p + scale_fill_manual(values=my_colors) + 
  theme_bw() + theme(axis.text.y = element_text(size=20), 
                     axis.text.x=element_text(size=14), axis.title.x =element_text(size=14), 
                     axis.title.y = element_text(size=18), legend.text = element_text(size=12), 
                     legend.title = element_text(size=18))+rotate_x_text(size=7,angle = 90) + 
    labs(x = NULL, y = "% of clone")

Idents(clones) <- 'Clone_Dynamics'
table(clones@meta.data$Clone_Dynamics)

Pattern_1 Pattern_2 Pattern_3 Pattern_4 Pattern_5 Unknown_1 Unknown_2 
      553       675       693      2797      1032       167        48 

Function for plotting clone dynamics

clone_patterns <- c('Pattern_1', 'Pattern_2', 'Pattern_3', 'Pattern_4', 'Pattern_5', 'Unknown_1', 'Unknown_2')
pattern_colors <- c('orange', 'green2', 'red', 'royalblue2', 'purple', 'gray40', 'black')
names(pattern_colors) <- clone_patterns
plotCloneDyn <- function(clone_pattern, pattern_color, tit = "") {
    # Get cells of interest
    ss <- subset(clones, idents = clone_pattern)
    highlighted_cells <- rownames(ss@meta.data)
    
    # Plot all cells in grey, highlight the pattern in your chosen color
    DimPlot(clones, 
            reduction = "fa", 
            cells.highlight = highlighted_cells,
            sizes.highlight = 0.25,
            cols.highlight = pattern_color,
            cols = "grey90", 
            pt.size = 0.1) + 
    ggtitle(tit) & NoLegend() & NoAxes()
}

Fig 3e

ForceAtlas embedding showing representative clones of each pattern with cells colored by pattern

cdp <- (lapply(1:7, function(i) plotCloneDyn(clone_patterns[i], pattern_colors[i], tit=clone_patterns[i])))
p_combined <- ggarrange(plotlist = cdp, ncol = 4, nrow = 2)
p_combined

getCloneCellIDs <- function(id) {
    cellIDs <- rownames(subset(clones,idents=c(id))@meta.data)
}
clone_cell_ids <- lapply(setNames(clone_patterns, clone_patterns), getCloneCellIDs)
plotPhenoWithBackground <- function(clone_pattern, pheno_col, title = "", seurat_obj = clones) {
    # Check if 'fa' reduction exists
    if (!"fa" %in% names(seurat_obj@reductions)) {
        stop("The 'fa' reduction is not found in the Seurat object.")
    }

    # Extract FA coordinates and metadata
    coords <- as.data.frame(Embeddings(seurat_obj, reduction = "fa"))
    coords$cell <- rownames(coords)

    # Get metadata
    meta <- seurat_obj@meta.data[, c("Clone_Dynamics", "Pheno")]
    meta$cell <- rownames(meta)

    # Merge coordinates and metadata
    dat <- merge(coords, meta, by = "cell")

    # Flag highlighted cells
    dat$highlight <- dat$Clone_Dynamics == clone_pattern
    dat$Pheno <- droplevels(dat$Pheno)

    # Split background and foreground
    bg_dat <- dat[!dat$highlight, ]
    fg_dat <- dat[dat$highlight, ]

    if (nrow(fg_dat) == 0) {
        warning(paste("No cells found for Clone_Dynamics pattern:", clone_pattern))
        return(ggplot() + ggtitle(paste(clone_pattern, "(no cells)")))
    }

    # Plot
    p <- ggplot() +
        geom_point(data = bg_dat, aes(x = FA_1, y = FA_2), color = "gray90", size = 0.2) +
        geom_point(data = fg_dat, aes(x = FA_1, y = FA_2, color = Pheno), size = 1) +
        scale_color_manual(values = pheno_col, drop = FALSE) +
        ggtitle(title) +
        theme_void() +
        theme(
            legend.position = "none",
            panel.background = element_rect(fill = "transparent", color = NA),
            plot.background = element_rect(fill = "transparent", color = NA)
        )

    return(p)
}

ForceAtlas embedding showing cells colored by SCLC phenotype

DimPlot(clones, group.by='Pheno', cols=pheno_col, reduction='fa', label=FALSE, label.size=6, pt.size = 0.05) & 
    NoAxes()

Fig 3f

ForceAtlas embedding showing representative clones of each pattern with cells colored by SCLC phenotype

plots <- lapply(clone_patterns, function(pat) plotPhenoWithBackground(pat, pheno_col, title = pat))
p_combined <- ggarrange(plotlist = plots, ncol = 4, nrow = 2)
p_combined

Visualize individual clones

Ext Data Fig. 6b,c

plotPatternDynByClone <- function(clone_pattern) {
    Idents(clones)<-'Clone_Dynamics'
    p1 <- subset(clones,idents=c(clone_pattern))
    
    pattern_color <- pattern_colors[clone_pattern]

    test <- as.data.frame(p1$CellTag_Clone)
    test$Barcodes <- rownames(test)
    # Group by CellTag_Clone
    test <- test %>% group_by(p1$CellTag_Clone)
    test <- dplyr::group_split(test)
    
    n_clones <- length(test)
    # Plot in for loop all RPM clones in Pattern 1
    plot_lst <- vector("list", length = n_clones)
    for (i in seq(n_clones)) {
      g <- DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', order=TRUE, 
                   cells.highlight=test[[i]]$Barcodes,sizes.highlight=2, 
                   cols.highlight=pattern_color) +
          ggtitle(paste0(test[[i]]$`p1$CellTag_Clone`[1])) + 
          theme(plot.title = element_text(size = 8,face = "plain")) & NoLegend() & 
          NoAxes()
      plot_lst[[i]] <- g
    }
    
    # Combine multiple plots for output, as desired
    return(cowplot::plot_grid(plotlist = plot_lst, ncol=5))
}
pattern_plot_list <- lapply(clone_patterns, plotPatternDynByClone)

n_plots_per_pattern <- sapply(pattern_plot_list, function(x) length(x[['layers']]))

## calculate height of figure based on number of plots per pattern
plt_height <- 2.667 * ((n_plots_per_pattern %/% 5) + 1)

Ext Data Fig 6b,c

pattern_plot_list[[1]]

pattern_plot_list[[2]]

pattern_plot_list[[3]]

pattern_plot_list[[4]]

pattern_plot_list[[5]]

pattern_plot_list[[6]]

pattern_plot_list[[7]]

Visualize only clones matching in vivo in FA projection

Ext Data Fig. 7f

Idents(clones) <- "CellTag_Clone"
# Subset just the clones that match the in vitro, pattern 1
invitromatch <- subset(clones, idents=c("RPM_Clone_14","RPM_Clone_2","RPM_Clone_33","RPM_Clone_36","RPM_Clone_6"))

p1_cells <- colnames(invitromatch)
p2_cells <- colnames(subset(clones, idents="RPM_Clone_23"))
p5_cells <- colnames(subset(clones, idents="RPM_Clone_13"))

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', 
        order=TRUE, cells.highlight=p1_cells, sizes.highlight=2, 
        cols.highlight=c("orange")) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', 
        order=TRUE, cells.highlight=p2_cells, sizes.highlight=2, 
        cols.highlight=pattern_colors["Pattern_2"]) + ggtitle("") & NoLegend() & NoAxes()

DimPlot(TBO_seurat, group.by="CellTag_Clone", reduction='fa', 
        order=TRUE, cells.highlight=p5_cells, sizes.highlight=2, 
        cols.highlight=pattern_colors["Pattern_5"]) + ggtitle("") & NoLegend() & NoAxes()

NA
NA

Fig. 3g

dpt psuedotime in FA space

FeaturePlot(TBO_seurat, features = c("dpt_pseudotime"), pt.size=0.01,
            reduction='fa',) + scale_color_viridis(option="viridis",direction=-1)& NoAxes()

Function to plot clone dynamics colored by DPT

plotCloneDynDpt <- function(clone_pattern, seurat_obj, title = "") {
    # Subset cells of interest
    ss <- subset(seurat_obj, idents = clone_pattern)
    highlight_cells <- colnames(ss)

    # Extract embeddings and metadata
    fa_coords <- Embeddings(seurat_obj, "fa")
    dpt_vals <- seurat_obj@meta.data$dpt_pseudotime
    names(dpt_vals) <- rownames(seurat_obj@meta.data)

    dat <- as.data.frame(fa_coords)
    dat$highlight <- ifelse(rownames(dat) %in% highlight_cells, "yes", "no")
    dat$pseudotime <- dpt_vals[rownames(dat)]
    colnames(dat)[1:2] <- c("FA1", "FA2")  # name columns for clarity

    library(ggplot2)
    ggplot(dat, aes(x = FA1, y = FA2)) +
        # Background cells
        geom_point(data = subset(dat, highlight == "no"), 
                   color = "grey85", size = 0.1) +
        # Highlighted cells colored by pseudotime
        geom_point(data = subset(dat, highlight == "yes"), 
                   aes(color = pseudotime), size = 0.25) +
        scale_color_viridis_c(option = "turbo", direction = -1) +
        ggtitle(title) +
        theme_void() +
        theme(
            legend.position = "none",
            panel.border = element_blank(),      # removes any panel border
            plot.background = element_rect(fill = "transparent", color = NA),
            panel.background = element_rect(fill = "transparent", color = NA),
            panel.grid.major = element_blank(),
            panel.grid.minor = element_blank()
        )
}

Fig 3h

Idents(clones) <- 'Clone_Dynamics'

my_pats <- c('Pattern_1','Pattern_2','Pattern_5','Unknown_1','Pattern_3','Pattern_4')

plots <- lapply(my_pats, function(pat) {
    tit <- gsub(pat, '_', ' ')
    plotCloneDynDpt(pat, clones, title = tit)
})

cowplot::plot_grid(plotlist = plots, ncol=6)

LS0tCnRpdGxlOiAiUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZyBub3RlYm9vayIKYXV0aG9yOiAiQWJiaWUgSXJlbGFuZCwgRGFycmVuIFR5c29uIgpkYXRlOiAiMjAyNS0wNi0yNCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIE1vZGlmeWluZyBvcmlnaW5hbCBjb2RlCk9yaWdpbmFsIGZpbGU6IFtGaWczXzRfNl9FeHRGaWc1LTYtMTBfUlBNX1JQTUFfQWxsb3NfQ2VsbFRhZy5SXShodHRwczovL2dpdGh1Yi5jb20vVEdPbGl2ZXItbGFiL0lyZWxhbmRfQmFzYWxfU0NMQ18yMDI1L2Jsb2IvbWFpbi9SX0NvZGUvRmlnM180XzZfRXh0RmlnNS02LTEwX1JQTV9SUE1BX0FsbG9zX0NlbGxUYWcuUikgIAoKT25seSBDZWxsVGFnIGFuYWx5c2lzIGluY2x1ZGVkIGluIHRoaXMgbm90ZWJvb2suCgojIyMgUmVsYXRlZCB0bzoKKiBGaWcgM2MtaAoqIEV4dGVuZGVkIERhdGEgRmlnIDZiLGMKKiBFeHRlbmRlZCBEYXRhIEZpZyA3ZgoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgICBsaWJyYXJ5KFNldXJhdCkKICAgIGxpYnJhcnkoU2V1cmF0T2JqZWN0KQogICAgbGlicmFyeShTdW1tYXJpemVkRXhwZXJpbWVudCkKICAgIGxpYnJhcnkoZ2dwbG90MikKICAgIGxpYnJhcnkoZ2dwdWJyKQogICAgbGlicmFyeSh0aWR5cikKICAgIGxpYnJhcnkocGF0Y2h3b3JrKQogICAgbGlicmFyeSh2aXJpZGlzKQp9KQpgYGAKYGBge3J9ClNBVkVGSUdTIDwtIEZBTFNFCmBgYAoKCmBgYHtyfQpUQk9fc2V1cmF0PC1yZWFkUkRTKCIuLi9kYXRhLzA1XzIwMjVfUlBNX1JQTUFfVEJPX0NlbGxUYWdfU2V1cmF0X3dTaWdzX0ZBX2RwdF9maW5hbC5yZHMiKQpUQk9fc2V1cmF0CmBgYAoKYGBge3J9CmNsb25lcyA8LSByZWFkUkRTKCIuLi9kYXRhLzA1XzIwMjVfUlBNX1JQTUFfVEJPQWxsb19DZWxsVGFnQ2xvbmVzX09ubHljbG9uZXMucmRzIikKYGBgCgpgYGB7cn0KbXlfY29sb3JzIDwtIGMoCiAgIiNFNDFBMUMiLCAjIHN0cm9uZyByZWQKICAiIzM3N0VCOCIsICMgbWVkaXVtIGJsdWUKICAiIzREQUY0QSIsICMgZ3JlZW4KICAiIzk4NEVBMyIsICMgcHVycGxlCiAgIiNGRjdGMDAiLCAjIG9yYW5nZQogICIjRkZGRjMzIiwgIyB5ZWxsb3cKICAiI0E2NTYyOCIsICMgYnJvd24KICAiI2U3Mjk4YSIsICMgcGluawogICIjNjY2NjY2IiwgIyBncmV5CiAgIiM2NkMyQTUiLCAjIHRlYWwKICAiI0ZDOEQ2MiIsICMgc2FsbW9uCiAgIiM4REEwQ0IiLCAjIHNvZnQgYmx1ZQogICIjRTc4QUMzIiwgIyBzb2Z0IHBpbmsgKGRpZmZlcmVudCBmcm9tIDgpCiAgIiNBNkQ4NTQiLCAjIGxpZ2h0IGdyZWVuIChidXQgeWVsbG93aXNoIHRpbnQsIG5vdCBncmVlbikKICAiI0ZGRDkyRiIsICMgbGVtb24geWVsbG93CiAgIiNFNUM0OTQiLCAjIGxpZ2h0IGJyb3duCiAgIiNCM0IzQjMiLCAjIGxpZ2h0IGdyZXkKICAiIzFCOUU3NyIsICMgZGVlcCB0ZWFsCiAgIiNEOTVGMDIiLCAjIGRhcmsgb3JhbmdlCiAgIiM3NTcwQjMiLCAjIHN0cm9uZyBwdXJwbGUKICAiIzY2QTYxRSIgICMgb2xpdmUgZ3JlZW4gKE5PVCBzYW1lIGdyZWVuIGFzIGJlZm9yZSkKKQoKcGhlbm9fY29sIDwtIGMoImJyb3duMiIsImRhcmtvcmNoaWQ0IiwiZG9kZ2VyYmx1ZSIsIiM2NkE2MUUiLCJvcmFuZ2UiLCJ0dXJxdW9pc2U0IiwidHVycXVvaXNlIikKbmFtZXMocGhlbm9fY29sKSA8LSBjKCJORSIsIk5FL05ldXJvbmFsIiwiTmV1cm9uYWwiLCJBVE9IMSIsIlR1ZnQiLCJUcmlwbGUtTmVnIiwiQmFzYWwiKQpgYGAKCiMjIyMgRmlnIDNjCkZvcmNlQXRsYXMgZW1iZWRkaW5nIG9mIFJQTSBhbmQgUlBNQSBDZWxsVGFnZ2VkIGFsbG9ncmFmdGVkIHR1bW9yIGNlbGxzCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9J2xlaWRlbl9zY1ZJXzEuMicsIGNvbHM9bXlfY29sb3JzLCByZWR1Y3Rpb249J2ZhJywgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZT00KSAmIAogICAgTm9BeGVzKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSAjIHN1cHByZXNzIGxlZ2VuZAoKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9J1BoZW5vJywgY29scz1waGVub19jb2wsIHJlZHVjdGlvbj0nZmEnLCBsYWJlbD1GQUxTRSwgbGFiZWwuc2l6ZT02KSAmIAogICAgTm9BeGVzKCkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD02LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEV4dHJhY3QgY29vcmRpbmF0ZXMgZnJvbSBmb3JjZS1kaXJlY3RlZCBsYXlvdXQgKG9yIGFueSBsYXlvdXQpCmRhdCA8LSBFbWJlZGRpbmdzKFRCT19zZXVyYXQsIHJlZHVjdGlvbiA9ICJmYSIpICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgbXV0YXRlKENsdXN0ZXIgPSBUQk9fc2V1cmF0JGxlaWRlbl9zY1ZJXzEuMiwKICAgICAgICAgR2Vub3R5cGUgPSBUQk9fc2V1cmF0JEdlbm90eXBlKQoKIyBTZXQgdXAgeW91ciBjb2xvciB2ZWN0b3IgKG5hbWVkKQpteV9jb2xvcnNfbmFtZWQgPC0gc2V0TmFtZXMobXlfY29sb3JzLCBsZXZlbHMoVEJPX3NldXJhdCRsZWlkZW5fc2NWSV8xLjIpKQoKIyBQbG90IGVhY2ggR2Vub3R5cGUgd2l0aCBiYWNrZ3JvdW5kIGNlbGxzIGdyZXllZApwbG90cyA8LSBsYXBwbHkodW5pcXVlKGRhdCRHZW5vdHlwZSksIGZ1bmN0aW9uKGcpIHsKICBkYXQkaGlnaGxpZ2h0IDwtIGlmZWxzZShkYXQkR2Vub3R5cGUgPT0gZywgImhpZ2hsaWdodCIsICJiYWNrZ3JvdW5kIikKICBkYXQkQ29sb3IgPC0gaWZlbHNlKGRhdCRHZW5vdHlwZSA9PSBnLCBteV9jb2xvcnNfbmFtZWRbZGF0JENsdXN0ZXJdLCAibGlnaHRncmV5IikKCiAgZ2dwbG90KGRhdCwgYWVzKHggPSBGQV8xLCB5ID0gRkFfMikpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQ29sb3IpLCBzaXplID0gMC41KSArCiAgICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsKICAgIGdndGl0bGUoZykgKwogICAgdGhlbWVfdm9pZCgpICsKICAgIHRoZW1lKAogICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpCiAgICApCn0pCgojIENvbWJpbmUgaW50byBvbmUgZmlndXJlCmEgPC0gd3JhcF9wbG90cyhwbG90cywgbmNvbCA9IDIpCmEKCmlmKFNBVkVGSUdTKSBnZ3NhdmUoIkZBYnlHZW5vLnBuZyIsIHBsb3Q9IGEsIHdpZHRoPTYsIGhlaWdodD0zLCBkcGk9MzAwLCBiZyA9ICJ0cmFuc3BhcmVudCIpCmBgYAoKCgojIyMgVmlzdWFsaXppbmcgQ2VsbHRhZy9jbG9uZSBkYXRhCgpGcm9tIHRhYmxlIGFib3ZlLCBhZGRlZCB3aGV0aGVyIGNsb25lcyB3ZXJlIHJvYnVzdCBvciBub3QgIApSb2J1c3QgZGVmaW5lZCBhcyA+NSBjZWxscyBwZXIgY2xvbmUgcG9zdC1RQy4gIApDZWxsVGFnIG1ldGFkYXRhOiBjbG9uZSBpbmZvcm1hdGlvbiBjYW4gYWxzbyBiZSBmb3VuZCBpbiBTdXBwbGVtZW50YXJ5IFRhYmxlIDQgb2YgSXJlbGFuZCBldCBhbCwgMjAyNSAgCmBgYHtyfQpJZGVudHMoVEJPX3NldXJhdCkgPC0gJ1JvYnVzdCcKYGBgCgoKYGBge3J9CnRhYmxlKFRCT19zZXVyYXRAbWV0YS5kYXRhJFJvYnVzdCwgVEJPX3NldXJhdEBtZXRhLmRhdGEkVW5JRCkKdGFibGUoVEJPX3NldXJhdEBtZXRhLmRhdGEkUm9idXN0LCBUQk9fc2V1cmF0QG1ldGEuZGF0YSRHZW5vQ1QpCmBgYAojIyMgSWRlbnRpZnkgcm9idXN0IGNsb25lcwpgYGB7cn0KdGFibGUoY2xvbmVzQG1ldGEuZGF0YSRSb2J1c3QsIGNsb25lc0BtZXRhLmRhdGEkR2Vub3R5cGUpCmBgYAoKIyMjIEFzc2VzcyBjbG9uZXMgYnkgbGVpZGVuIGNsdXN0ZXIKIyMjIyBGaWcuIDNkICAKRmlyc3QsIGp1c3QgbG9vayBhdCBiYXIgZ3JhcGgsIG5vIHBhcnRpY3VsYXIgb3JkZXIgIwoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTB9CklkZW50cyhjbG9uZXMpIDwtICdsZWlkZW5fc2NWSV8xLjInCgp4IDwtIHRhYmxlKGNsb25lc0BtZXRhLmRhdGEkQ2VsbFRhZ19DbG9uZSxJZGVudHMoY2xvbmVzKSkKcHJvcG9ydGlvbnMgPC0gYXMuZGF0YS5mcmFtZSgxMDAqcHJvcC50YWJsZSh4LCBtYXJnaW4gPSAxKSkKCiMgcHJvcG9ydGlvbnMkQ2x1c3Rlcgpjb2xuYW1lcyhwcm9wb3J0aW9ucyk8LWMoIkNsdXN0ZXIiLCAiU2FtcGxlIiwgIkZyZXF1ZW5jeSIpCgojIFN0YWNrZWQKcCA8LSBnZ3Bsb3QocHJvcG9ydGlvbnMsIGFlcyhmaWxsPVNhbXBsZSwgeT1GcmVxdWVuY3ksIHg9Q2x1c3RlcikpICsgCiAgZ2VvbV9iYXIocG9zaXRpb249InN0YWNrIiwgc3RhdD0iaWRlbnRpdHkiKQoKcCArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teV9jb2xvcnMpICsgCiAgdGhlbWVfYncoKSsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIAogICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE0KSwgYXhpcy50aXRsZS54ID1lbGVtZW50X3RleHQoc2l6ZT0xNCksIAogICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSwgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksIAogICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSkrcm90YXRlX3hfdGV4dChzaXplPTcsYW5nbGUgPSA5MCkKCmRhdCA8LSBwJGRhdGEKYGBgClRyYW5zZm9ybSBkYXRhIHRvIHBlcmZvcm0gaGllcmFyY2hpY2FsIChhZ2dsb21lcmF0aXZlKSBjbHVzdGVyaW5nCmBgYHtyfQojIFBpdm90IHRvIHdpZGUgZm9ybWF0OiBDbHVzdGVyIMOXIFNhbXBsZQptYXQgPC0gZGF0ICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBTYW1wbGUsIHZhbHVlc19mcm9tID0gRnJlcXVlbmN5LCB2YWx1ZXNfZmlsbCA9IDApICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJDbHVzdGVyIikgJT4lCiAgYXMubWF0cml4KCkKCm1hdF9ub3JtIDwtIHByb3AudGFibGUobWF0LCBtYXJnaW4gPSAxKSAgIyBub3JtYWxpemUgZWFjaCByb3cgdG8gc3VtIHRvIDEKYGBgCgpIaWVyYXJjaGljYWwgKGFnZ2xvbWVyYXRpdmUpIGNsdXN0ZXJpbmcgb2YgY2xvbmVzIHdpdGggZGVmYXVsdCBwYXJhbWV0ZXJzIGluIGBwaGVhdG1hcCgpYApgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQpobSA8LSBwaGVhdG1hcDo6cGhlYXRtYXAodChtYXQpLCBjdXRyZWVfcm93cyA9IDEsIGN1dHJlZV9jb2xzID0gOCwgY2VsbHdpZHRoID0gNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsaGVpZ2h0ID0gNSwgZm9udHNpemUgPSA4LAogICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzPUZBTFNFLCBib3JkZXJfY29sb3I9TkEsIAogICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoImRhcmt0dXJxdW9pc2UiLCJibGFjayIsInJlZDIiKSkoMzApKQoKY2xvbmVfb3JkZXIgPC0gY29sbmFtZXModChtYXQpKVtobSR0cmVlX2NvbCRvcmRlcl0KYGBgCgoKIyMjIyBGaWcgM2QKQ2x1c3RlcmVkIGNsb25lcyBieSBwcm9wb3J0aW9ucyBvZiBjZWxscyBpbiBMZWlkZW4gY2x1c3RlcnMKYGBge3IgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTB9CiMgTm93LCBwbG90IGNsb25lcyBieSBMZWlkZW4sIG9yZGVyZWQsIGZpbmFsICMgKEZpZy4gM2QpCklkZW50cyhjbG9uZXMpPC0nbGVpZGVuX3NjVklfMS4yJwoKeCA8LSB0YWJsZShjbG9uZXNAbWV0YS5kYXRhJENlbGxUYWdfQ2xvbmUsSWRlbnRzKGNsb25lcykpCnByb3BvcnRpb25zIDwtIGFzLmRhdGEuZnJhbWUoMTAwKnByb3AudGFibGUoeCwgbWFyZ2luID0gMSkpCgpjb2xuYW1lcyhwcm9wb3J0aW9ucykgPC0gYygiQ2x1c3RlciIsICJTYW1wbGUiLCAiRnJlcXVlbmN5IikKcHJvcG9ydGlvbnMkQ2x1c3RlciA8LSBmYWN0b3IocHJvcG9ydGlvbnMkQ2x1c3RlciwgY2xvbmVfb3JkZXIpCgojIFN0YWNrZWQKcCA8LSBnZ3Bsb3QocHJvcG9ydGlvbnMsIGFlcyhmaWxsPVNhbXBsZSwgeT1GcmVxdWVuY3ksIHg9Q2x1c3RlcikpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0ic3RhY2siLCBzdGF0PSJpZGVudGl0eSIpCgpwICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15X2NvbG9ycykgKyAKICB0aGVtZV9idygpICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIAogICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xNCksIGF4aXMudGl0bGUueCA9ZWxlbWVudF90ZXh0KHNpemU9MTQpLCAKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTgpLCBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE4KSkrcm90YXRlX3hfdGV4dChzaXplPTcsYW5nbGUgPSA5MCkgKyAKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSAiJSBvZiBjbG9uZSIpCgpgYGAKCmBgYHtyfQpJZGVudHMoY2xvbmVzKSA8LSAnQ2xvbmVfRHluYW1pY3MnCnRhYmxlKGNsb25lc0BtZXRhLmRhdGEkQ2xvbmVfRHluYW1pY3MpCmBgYAoKIyMjIEZ1bmN0aW9uIGZvciBwbG90dGluZyBjbG9uZSBkeW5hbWljcwoKYGBge3J9CmNsb25lX3BhdHRlcm5zIDwtIGMoJ1BhdHRlcm5fMScsICdQYXR0ZXJuXzInLCAnUGF0dGVybl8zJywgJ1BhdHRlcm5fNCcsICdQYXR0ZXJuXzUnLCAnVW5rbm93bl8xJywgJ1Vua25vd25fMicpCnBhdHRlcm5fY29sb3JzIDwtIGMoJ29yYW5nZScsICdncmVlbjInLCAncmVkJywgJ3JveWFsYmx1ZTInLCAncHVycGxlJywgJ2dyYXk0MCcsICdibGFjaycpCm5hbWVzKHBhdHRlcm5fY29sb3JzKSA8LSBjbG9uZV9wYXR0ZXJucwpgYGAKCmBgYHtyfQpwbG90Q2xvbmVEeW4gPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybiwgcGF0dGVybl9jb2xvciwgdGl0ID0gIiIpIHsKICAgICMgR2V0IGNlbGxzIG9mIGludGVyZXN0CiAgICBzcyA8LSBzdWJzZXQoY2xvbmVzLCBpZGVudHMgPSBjbG9uZV9wYXR0ZXJuKQogICAgaGlnaGxpZ2h0ZWRfY2VsbHMgPC0gcm93bmFtZXMoc3NAbWV0YS5kYXRhKQogICAgCiAgICAjIFBsb3QgYWxsIGNlbGxzIGluIGdyZXksIGhpZ2hsaWdodCB0aGUgcGF0dGVybiBpbiB5b3VyIGNob3NlbiBjb2xvcgogICAgRGltUGxvdChjbG9uZXMsIAogICAgICAgICAgICByZWR1Y3Rpb24gPSAiZmEiLCAKICAgICAgICAgICAgY2VsbHMuaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0ZWRfY2VsbHMsCiAgICAgICAgICAgIHNpemVzLmhpZ2hsaWdodCA9IDAuMjUsCiAgICAgICAgICAgIGNvbHMuaGlnaGxpZ2h0ID0gcGF0dGVybl9jb2xvciwKICAgICAgICAgICAgY29scyA9ICJncmV5OTAiLCAKICAgICAgICAgICAgcHQuc2l6ZSA9IDAuMSkgKyAKICAgIGdndGl0bGUodGl0KSAmIE5vTGVnZW5kKCkgJiBOb0F4ZXMoKQp9CmBgYAoKCiMjIyMgRmlnIDNlCkZvcmNlQXRsYXMgZW1iZWRkaW5nIHNob3dpbmcgcmVwcmVzZW50YXRpdmUgY2xvbmVzIG9mIGVhY2ggcGF0dGVybiB3aXRoIGNlbGxzIGNvbG9yZWQgYnkgcGF0dGVybgpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpjZHAgPC0gKGxhcHBseSgxOjcsIGZ1bmN0aW9uKGkpIHBsb3RDbG9uZUR5bihjbG9uZV9wYXR0ZXJuc1tpXSwgcGF0dGVybl9jb2xvcnNbaV0sIHRpdD1jbG9uZV9wYXR0ZXJuc1tpXSkpKQpwX2NvbWJpbmVkIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IGNkcCwgbmNvbCA9IDQsIG5yb3cgPSAyKQpwX2NvbWJpbmVkCmBgYAoKYGBge3J9CmdldENsb25lQ2VsbElEcyA8LSBmdW5jdGlvbihpZCkgewogICAgY2VsbElEcyA8LSByb3duYW1lcyhzdWJzZXQoY2xvbmVzLGlkZW50cz1jKGlkKSlAbWV0YS5kYXRhKQp9CmNsb25lX2NlbGxfaWRzIDwtIGxhcHBseShzZXROYW1lcyhjbG9uZV9wYXR0ZXJucywgY2xvbmVfcGF0dGVybnMpLCBnZXRDbG9uZUNlbGxJRHMpCmBgYAoKCmBgYHtyfQpwbG90UGhlbm9XaXRoQmFja2dyb3VuZCA8LSBmdW5jdGlvbihjbG9uZV9wYXR0ZXJuLCBwaGVub19jb2wsIHRpdGxlID0gIiIsIHNldXJhdF9vYmogPSBjbG9uZXMpIHsKICAgICMgQ2hlY2sgaWYgJ2ZhJyByZWR1Y3Rpb24gZXhpc3RzCiAgICBpZiAoISJmYSIgJWluJSBuYW1lcyhzZXVyYXRfb2JqQHJlZHVjdGlvbnMpKSB7CiAgICAgICAgc3RvcCgiVGhlICdmYScgcmVkdWN0aW9uIGlzIG5vdCBmb3VuZCBpbiB0aGUgU2V1cmF0IG9iamVjdC4iKQogICAgfQoKICAgICMgRXh0cmFjdCBGQSBjb29yZGluYXRlcyBhbmQgbWV0YWRhdGEKICAgIGNvb3JkcyA8LSBhcy5kYXRhLmZyYW1lKEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgcmVkdWN0aW9uID0gImZhIikpCiAgICBjb29yZHMkY2VsbCA8LSByb3duYW1lcyhjb29yZHMpCgogICAgIyBHZXQgbWV0YWRhdGEKICAgIG1ldGEgPC0gc2V1cmF0X29iakBtZXRhLmRhdGFbLCBjKCJDbG9uZV9EeW5hbWljcyIsICJQaGVubyIpXQogICAgbWV0YSRjZWxsIDwtIHJvd25hbWVzKG1ldGEpCgogICAgIyBNZXJnZSBjb29yZGluYXRlcyBhbmQgbWV0YWRhdGEKICAgIGRhdCA8LSBtZXJnZShjb29yZHMsIG1ldGEsIGJ5ID0gImNlbGwiKQoKICAgICMgRmxhZyBoaWdobGlnaHRlZCBjZWxscwogICAgZGF0JGhpZ2hsaWdodCA8LSBkYXQkQ2xvbmVfRHluYW1pY3MgPT0gY2xvbmVfcGF0dGVybgogICAgZGF0JFBoZW5vIDwtIGRyb3BsZXZlbHMoZGF0JFBoZW5vKQoKICAgICMgU3BsaXQgYmFja2dyb3VuZCBhbmQgZm9yZWdyb3VuZAogICAgYmdfZGF0IDwtIGRhdFshZGF0JGhpZ2hsaWdodCwgXQogICAgZmdfZGF0IDwtIGRhdFtkYXQkaGlnaGxpZ2h0LCBdCgogICAgaWYgKG5yb3coZmdfZGF0KSA9PSAwKSB7CiAgICAgICAgd2FybmluZyhwYXN0ZSgiTm8gY2VsbHMgZm91bmQgZm9yIENsb25lX0R5bmFtaWNzIHBhdHRlcm46IiwgY2xvbmVfcGF0dGVybikpCiAgICAgICAgcmV0dXJuKGdncGxvdCgpICsgZ2d0aXRsZShwYXN0ZShjbG9uZV9wYXR0ZXJuLCAiKG5vIGNlbGxzKSIpKSkKICAgIH0KCiAgICAjIFBsb3QKICAgIHAgPC0gZ2dwbG90KCkgKwogICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IGJnX2RhdCwgYWVzKHggPSBGQV8xLCB5ID0gRkFfMiksIGNvbG9yID0gImdyYXk5MCIsIHNpemUgPSAwLjIpICsKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBmZ19kYXQsIGFlcyh4ID0gRkFfMSwgeSA9IEZBXzIsIGNvbG9yID0gUGhlbm8pLCBzaXplID0gMSkgKwogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwaGVub19jb2wsIGRyb3AgPSBGQUxTRSkgKwogICAgICAgIGdndGl0bGUodGl0bGUpICsKICAgICAgICB0aGVtZV92b2lkKCkgKwogICAgICAgIHRoZW1lKAogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpLAogICAgICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ0cmFuc3BhcmVudCIsIGNvbG9yID0gTkEpCiAgICAgICAgKQoKICAgIHJldHVybihwKQp9CmBgYAoKRm9yY2VBdGxhcyBlbWJlZGRpbmcgc2hvd2luZyBjZWxscyBjb2xvcmVkIGJ5IFNDTEMgcGhlbm90eXBlCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTQuNX0KRGltUGxvdChjbG9uZXMsIGdyb3VwLmJ5PSdQaGVubycsIGNvbHM9cGhlbm9fY29sLCByZWR1Y3Rpb249J2ZhJywgbGFiZWw9RkFMU0UsIGxhYmVsLnNpemU9NiwgcHQuc2l6ZSA9IDAuMDUpICYgCiAgICBOb0F4ZXMoKQpgYGAKCgojIyMjIEZpZyAzZgpGb3JjZUF0bGFzIGVtYmVkZGluZyBzaG93aW5nIHJlcHJlc2VudGF0aXZlIGNsb25lcyBvZiBlYWNoIHBhdHRlcm4gd2l0aCBjZWxscyBjb2xvcmVkIGJ5IFNDTEMgcGhlbm90eXBlCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBsb3RzIDwtIGxhcHBseShjbG9uZV9wYXR0ZXJucywgZnVuY3Rpb24ocGF0KSBwbG90UGhlbm9XaXRoQmFja2dyb3VuZChwYXQsIHBoZW5vX2NvbCwgdGl0bGUgPSBwYXQpKQpwX2NvbWJpbmVkIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IHBsb3RzLCBuY29sID0gNCwgbnJvdyA9IDIpCnBfY29tYmluZWQKYGBgCgoKIyMjIFZpc3VhbGl6ZSBpbmRpdmlkdWFsIGNsb25lcwpFeHQgRGF0YSBGaWcuIDZiLGMKYGBge3J9CnBsb3RQYXR0ZXJuRHluQnlDbG9uZSA8LSBmdW5jdGlvbihjbG9uZV9wYXR0ZXJuKSB7CiAgICBJZGVudHMoY2xvbmVzKTwtJ0Nsb25lX0R5bmFtaWNzJwogICAgcDEgPC0gc3Vic2V0KGNsb25lcyxpZGVudHM9YyhjbG9uZV9wYXR0ZXJuKSkKICAgIAogICAgcGF0dGVybl9jb2xvciA8LSBwYXR0ZXJuX2NvbG9yc1tjbG9uZV9wYXR0ZXJuXQoKICAgIHRlc3QgPC0gYXMuZGF0YS5mcmFtZShwMSRDZWxsVGFnX0Nsb25lKQogICAgdGVzdCRCYXJjb2RlcyA8LSByb3duYW1lcyh0ZXN0KQogICAgIyBHcm91cCBieSBDZWxsVGFnX0Nsb25lCiAgICB0ZXN0IDwtIHRlc3QgJT4lIGdyb3VwX2J5KHAxJENlbGxUYWdfQ2xvbmUpCiAgICB0ZXN0IDwtIGRwbHlyOjpncm91cF9zcGxpdCh0ZXN0KQogICAgCiAgICBuX2Nsb25lcyA8LSBsZW5ndGgodGVzdCkKICAgICMgUGxvdCBpbiBmb3IgbG9vcCBhbGwgUlBNIGNsb25lcyBpbiBQYXR0ZXJuIDEKICAgIHBsb3RfbHN0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aCA9IG5fY2xvbmVzKQogICAgZm9yIChpIGluIHNlcShuX2Nsb25lcykpIHsKICAgICAgZyA8LSBEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIG9yZGVyPVRSVUUsIAogICAgICAgICAgICAgICAgICAgY2VsbHMuaGlnaGxpZ2h0PXRlc3RbW2ldXSRCYXJjb2RlcyxzaXplcy5oaWdobGlnaHQ9MiwgCiAgICAgICAgICAgICAgICAgICBjb2xzLmhpZ2hsaWdodD1wYXR0ZXJuX2NvbG9yKSArCiAgICAgICAgICBnZ3RpdGxlKHBhc3RlMCh0ZXN0W1tpXV0kYHAxJENlbGxUYWdfQ2xvbmVgWzFdKSkgKyAKICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsZmFjZSA9ICJwbGFpbiIpKSAmIE5vTGVnZW5kKCkgJiAKICAgICAgICAgIE5vQXhlcygpCiAgICAgIHBsb3RfbHN0W1tpXV0gPC0gZwogICAgfQogICAgCiAgICAjIENvbWJpbmUgbXVsdGlwbGUgcGxvdHMgZm9yIG91dHB1dCwgYXMgZGVzaXJlZAogICAgcmV0dXJuKGNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RfbHN0LCBuY29sPTUpKQp9CmBgYAoKYGBge3J9CnBhdHRlcm5fcGxvdF9saXN0IDwtIGxhcHBseShjbG9uZV9wYXR0ZXJucywgcGxvdFBhdHRlcm5EeW5CeUNsb25lKQoKbl9wbG90c19wZXJfcGF0dGVybiA8LSBzYXBwbHkocGF0dGVybl9wbG90X2xpc3QsIGZ1bmN0aW9uKHgpIGxlbmd0aCh4W1snbGF5ZXJzJ11dKSkKCiMjIGNhbGN1bGF0ZSBoZWlnaHQgb2YgZmlndXJlIGJhc2VkIG9uIG51bWJlciBvZiBwbG90cyBwZXIgcGF0dGVybgpwbHRfaGVpZ2h0IDwtIDIuNjY3ICogKChuX3Bsb3RzX3Blcl9wYXR0ZXJuICUvJSA1KSArIDEpCmBgYAoKIyMjIyBFeHQgRGF0YSBGaWcgNmIsYwpgYGB7ciBmaWcuaGVpZ2h0PTE2LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1sxXV0KYGBgCgoKYGBge3IgZmlnLmhlaWdodD01LjMzLCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1syXV0KYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTAuNjY3LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1szXV0KYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTMuMzMzLCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s0XV0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTIuNjY3LCBmaWcud2lkdGg9MTJ9CnBhdHRlcm5fcGxvdF9saXN0W1s1XV0KYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTUuMzMsIGZpZy53aWR0aD0xMn0KcGF0dGVybl9wbG90X2xpc3RbWzZdXQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9Mi42NjcsIGZpZy53aWR0aD0xMn0KcGF0dGVybl9wbG90X2xpc3RbWzddXQpgYGAKIyMgVmlzdWFsaXplIG9ubHkgY2xvbmVzIG1hdGNoaW5nIGluIHZpdm8gaW4gRkEgcHJvamVjdGlvbgpFeHQgRGF0YSBGaWcuIDdmCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0zfQpJZGVudHMoY2xvbmVzKSA8LSAiQ2VsbFRhZ19DbG9uZSIKIyBTdWJzZXQganVzdCB0aGUgY2xvbmVzIHRoYXQgbWF0Y2ggdGhlIGluIHZpdHJvLCBwYXR0ZXJuIDEKaW52aXRyb21hdGNoIDwtIHN1YnNldChjbG9uZXMsIGlkZW50cz1jKCJSUE1fQ2xvbmVfMTQiLCJSUE1fQ2xvbmVfMiIsIlJQTV9DbG9uZV8zMyIsIlJQTV9DbG9uZV8zNiIsIlJQTV9DbG9uZV82IikpCgpwMV9jZWxscyA8LSBjb2xuYW1lcyhpbnZpdHJvbWF0Y2gpCnAyX2NlbGxzIDwtIGNvbG5hbWVzKHN1YnNldChjbG9uZXMsIGlkZW50cz0iUlBNX0Nsb25lXzIzIikpCnA1X2NlbGxzIDwtIGNvbG5hbWVzKHN1YnNldChjbG9uZXMsIGlkZW50cz0iUlBNX0Nsb25lXzEzIikpCgpEaW1QbG90KFRCT19zZXVyYXQsIGdyb3VwLmJ5PSJDZWxsVGFnX0Nsb25lIiwgcmVkdWN0aW9uPSdmYScsIAogICAgICAgIG9yZGVyPVRSVUUsIGNlbGxzLmhpZ2hsaWdodD1wMV9jZWxscywgc2l6ZXMuaGlnaGxpZ2h0PTIsIAogICAgICAgIGNvbHMuaGlnaGxpZ2h0PWMoIm9yYW5nZSIpKSArIGdndGl0bGUoIiIpICYgTm9MZWdlbmQoKSAmIE5vQXhlcygpCkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9IkNlbGxUYWdfQ2xvbmUiLCByZWR1Y3Rpb249J2ZhJywgCiAgICAgICAgb3JkZXI9VFJVRSwgY2VsbHMuaGlnaGxpZ2h0PXAyX2NlbGxzLCBzaXplcy5oaWdobGlnaHQ9MiwgCiAgICAgICAgY29scy5oaWdobGlnaHQ9cGF0dGVybl9jb2xvcnNbIlBhdHRlcm5fMiJdKSArIGdndGl0bGUoIiIpICYgTm9MZWdlbmQoKSAmIE5vQXhlcygpCkRpbVBsb3QoVEJPX3NldXJhdCwgZ3JvdXAuYnk9IkNlbGxUYWdfQ2xvbmUiLCByZWR1Y3Rpb249J2ZhJywgCiAgICAgICAgb3JkZXI9VFJVRSwgY2VsbHMuaGlnaGxpZ2h0PXA1X2NlbGxzLCBzaXplcy5oaWdobGlnaHQ9MiwgCiAgICAgICAgY29scy5oaWdobGlnaHQ9cGF0dGVybl9jb2xvcnNbIlBhdHRlcm5fNSJdKSArIGdndGl0bGUoIiIpICYgTm9MZWdlbmQoKSAmIE5vQXhlcygpCgoKYGBgCgpgYGB7cn0KCmBgYAoKCiMjIyMgRmlnLiAzZyAKZHB0IHBzdWVkb3RpbWUgaW4gRkEgc3BhY2UgCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTMuNzUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CkZlYXR1cmVQbG90KFRCT19zZXVyYXQsIGZlYXR1cmVzID0gYygiZHB0X3BzZXVkb3RpbWUiKSwgcHQuc2l6ZT0wLjAxLAogICAgICAgICAgICByZWR1Y3Rpb249J2ZhJywpICsgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb249InZpcmlkaXMiLGRpcmVjdGlvbj0tMSkmIE5vQXhlcygpCgpgYGAKIyMjIyBGdW5jdGlvbiB0byBwbG90IGNsb25lIGR5bmFtaWNzIGNvbG9yZWQgYnkgRFBUCmBgYHtyfQpwbG90Q2xvbmVEeW5EcHQgPC0gZnVuY3Rpb24oY2xvbmVfcGF0dGVybiwgc2V1cmF0X29iaiwgdGl0bGUgPSAiIikgewogICAgIyBTdWJzZXQgY2VsbHMgb2YgaW50ZXJlc3QKICAgIHNzIDwtIHN1YnNldChzZXVyYXRfb2JqLCBpZGVudHMgPSBjbG9uZV9wYXR0ZXJuKQogICAgaGlnaGxpZ2h0X2NlbGxzIDwtIGNvbG5hbWVzKHNzKQoKICAgICMgRXh0cmFjdCBlbWJlZGRpbmdzIGFuZCBtZXRhZGF0YQogICAgZmFfY29vcmRzIDwtIEVtYmVkZGluZ3Moc2V1cmF0X29iaiwgImZhIikKICAgIGRwdF92YWxzIDwtIHNldXJhdF9vYmpAbWV0YS5kYXRhJGRwdF9wc2V1ZG90aW1lCiAgICBuYW1lcyhkcHRfdmFscykgPC0gcm93bmFtZXMoc2V1cmF0X29iakBtZXRhLmRhdGEpCgogICAgZGF0IDwtIGFzLmRhdGEuZnJhbWUoZmFfY29vcmRzKQogICAgZGF0JGhpZ2hsaWdodCA8LSBpZmVsc2Uocm93bmFtZXMoZGF0KSAlaW4lIGhpZ2hsaWdodF9jZWxscywgInllcyIsICJubyIpCiAgICBkYXQkcHNldWRvdGltZSA8LSBkcHRfdmFsc1tyb3duYW1lcyhkYXQpXQogICAgY29sbmFtZXMoZGF0KVsxOjJdIDwtIGMoIkZBMSIsICJGQTIiKSAgIyBuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKCiAgICBsaWJyYXJ5KGdncGxvdDIpCiAgICBnZ3Bsb3QoZGF0LCBhZXMoeCA9IEZBMSwgeSA9IEZBMikpICsKICAgICAgICAjIEJhY2tncm91bmQgY2VsbHMKICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGF0LCBoaWdobGlnaHQgPT0gIm5vIiksIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JleTg1Iiwgc2l6ZSA9IDAuMSkgKwogICAgICAgICMgSGlnaGxpZ2h0ZWQgY2VsbHMgY29sb3JlZCBieSBwc2V1ZG90aW1lCiAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRhdCwgaGlnaGxpZ2h0ID09ICJ5ZXMiKSwgCiAgICAgICAgICAgICAgICAgICBhZXMoY29sb3IgPSBwc2V1ZG90aW1lKSwgc2l6ZSA9IDAuMjUpICsKICAgICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gInR1cmJvIiwgZGlyZWN0aW9uID0gLTEpICsKICAgICAgICBnZ3RpdGxlKHRpdGxlKSArCiAgICAgICAgdGhlbWVfdm9pZCgpICsKICAgICAgICB0aGVtZSgKICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksICAgICAgIyByZW1vdmVzIGFueSBwYW5lbCBib3JkZXIKICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiLCBjb2xvciA9IE5BKSwKICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgY29sb3IgPSBOQSksCiAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkKICAgICAgICApCn0KYGBgCgoKCiMjIyMgRmlnIDNoCmBgYHtyIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTEyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpJZGVudHMoY2xvbmVzKSA8LSAnQ2xvbmVfRHluYW1pY3MnCgpteV9wYXRzIDwtIGMoJ1BhdHRlcm5fMScsJ1BhdHRlcm5fMicsJ1BhdHRlcm5fNScsJ1Vua25vd25fMScsJ1BhdHRlcm5fMycsJ1BhdHRlcm5fNCcpCgpwbG90cyA8LSBsYXBwbHkobXlfcGF0cywgZnVuY3Rpb24ocGF0KSB7CiAgICB0aXQgPC0gZ3N1YihwYXQsICdfJywgJyAnKQogICAgcGxvdENsb25lRHluRHB0KHBhdCwgY2xvbmVzLCB0aXRsZSA9IHRpdCkKfSkKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RzLCBuY29sPTYpCmBgYAoK